﻿/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2012 Brno University of Technology - Faculty of Information Technology (http://www.fit.vutbr.cz)
 * Author(s):
 * Vladimir Vesely (mailto:ivesely@fit.vutbr.cz)
 * Martin Mares
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Linq;
using System.IO;
using System.Globalization;

namespace PcapMerger
{
    /// <remarks>
    /// Class implementing support for TCPD (Pcap) files
    /// </remarks>
    class PmCapturePcap : PmCaptureBase
    {

        #region LinkTypeEnumeration
        /// <summary>
        /// Enumeration of TCPDump link types
        /// </summary>
        public enum TCPDLinkType
        {
            Null = 0,
            Ethernet = 1,
            IEEE8025 = 6,
            ArcnetBsd = 7,
            SLIP = 8,
            PPP = 9,
            FDDI = 10,
            PPPHDLC = 50,
            PPPEthernet = 51,
            ATMRfc1483 = 100,
            Raw = 101,
            CiscoHDLC = 104,
            IEEE80211 = 105,
            FrameRelay = 107,
            Loop = 108,
            LinuxSll = 113,
            LTALK = 114,
            PFLOG = 117,
            IEEE80211Prism = 119,
            IPOverFC = 122,
            SunATM = 123,
            IEEE80211Radiotap = 127,
            ArcnetLinux = 129,
            AppleIPOverIEEE1394 = 138,
            MTP2WithPhdr = 139,
            MTP2 = 140,
            MTP3 = 141,
            SCCP = 142,
            DOCSIS = 143,
            LinuxIRDA = 144,
            IEEE80211Avs = 163,
            BacnetMsTp = 165,
            PPPPppd = 166,
            GPRSLLC = 169,
            LinuxLAPD = 177,
            BluetoothHciH4 = 187,
            USBLinux = 189,
            PPI = 192,
            IEEE802154 = 195,
            SITA = 196,
            ERF = 197,
            BluetoothHciH4WithPhdr = 201,
            AX25Kiss = 202,
            LAPD = 203,
            PPPWithDir = 204,
            CiscoHDLCWithDir = 205,
            FrameRelayWithDir = 206,
            IpmbLinux = 209,
            IEEE802154NonaskPhy = 215,
            USBLinuxMmapped = 220,
            FC2 = 224,
            FC2WithFrameDelims = 225,
            IPNET = 226,
            CanSocketcan = 227,
            IPV4 = 228,
            IPV6 = 229,
            IEEE802154Nofcs = 230,
            DBUS = 231,
            DVBCi = 235,
            MUX27010 = 236,
            Stanag5066DPdu = 237,
            NFLOG = 239,
            Netanalyzer = 240,
            NetanalyzerTransparent = 241,
            IPOib = 242,
            MPEG2TS = 243,
            LinktypeNg40 = 244,
            LinktypeNfcLlcp = 245,
        }
        #endregion

        #region PcapFileVariables

        private UInt16 _versionMaj;
        private UInt16 _versionMin;
        private TCPDLinkType _pcapLinkType;
        private Int32 _tcpdTimeZoneOffset;
        private UInt32 _sigfigs;
        private UInt32 _snaplen;

        PmSupportedTypes.LinkTypes linkType = PmSupportedTypes.LinkTypes.Null;

        #endregion

        #region Constructors

        /// <summary>
        /// Basic constructor - generaly for creating output file
        /// </summary>
        public PmCapturePcap() :
            base(PmSupportedTypes.CaptureFileTypes.LibPCAP)
        {

        }

        /// <summary>
        /// Load constructor - generaly for loading input from file
        /// (no need to call OpenFile after using this constructor)
        /// </summary>
        public PmCapturePcap(String filePath, BinaryReader fileReader) :
            base(filePath, fileReader, PmSupportedTypes.CaptureFileTypes.LibPCAP)
        {
            ParseTCPDHeader();
            linkType = ConvertPcapLayer2ToCommonLayer2(_pcapLinkType);

        }

        #endregion

        /// <summary>
        /// First timestamp for LibPcap is timestamp of first frame
        /// </summary>
        /// <returns>Returns DateTime of first frame</returns>
        override public DateTime? FirstTimeStamp()
        {
            //Read timestamp of first frame on offset 24
            return ConvertUnixTimeToDate(ConvertTCPDToUnixTime(
                (Int32) (GetTCPDPckTimestampSeconds(24) + _tcpdTimeZoneOffset),
                (Int32) GetTCPDPckTimestampUseconds(24)
                                             )
                );
        }

        #region IPMCaptureFileIOMethods

        /// <summary>
        /// Initialize FrameTables and appropriate FrameVectors from input file
        /// </summary>
        override public void CreateFrameTable()
        {
            if (BinReader != null)
            {
                ParseTCPDFrameTable();
            }
        }

        /// <summary>
        /// Store frames contained in Frame Table to PCAP file
        /// </summary>
        override public bool CreateOutput(String fileName)
        {
            return CreateTCPDOutput(fileName);
        }

        #endregion

        #region TCPDtoSupprotedtypesConversionMethods

        private PmSupportedTypes.LinkTypes ConvertPcapLayer2ToCommonLayer2(TCPDLinkType linkaType)
        {
            switch (linkaType)
            {
                case TCPDLinkType.Ethernet: return PmSupportedTypes.LinkTypes.Ethernet;
                case TCPDLinkType.FDDI: return PmSupportedTypes.LinkTypes.FDDI;
                case TCPDLinkType.Raw: return PmSupportedTypes.LinkTypes.Raw;
                case TCPDLinkType.IEEE80211: return PmSupportedTypes.LinkTypes.IEEE80211;
                case TCPDLinkType.ATMRfc1483: return PmSupportedTypes.LinkTypes.ATMRfc1483;
                default: return PmSupportedTypes.LinkTypes.Null;
            }
        }

        private TCPDLinkType ConvertCommonLayer2ToPcapLayer2(PmSupportedTypes.LinkTypes linkaType)
        {
            switch (linkaType)
            {
                case PmSupportedTypes.LinkTypes.Ethernet: return TCPDLinkType.Ethernet;
                case PmSupportedTypes.LinkTypes.FDDI: return TCPDLinkType.FDDI;
                case PmSupportedTypes.LinkTypes.Raw: return TCPDLinkType.Raw;
                case PmSupportedTypes.LinkTypes.IEEE80211: return TCPDLinkType.IEEE80211;
                case PmSupportedTypes.LinkTypes.ATMRfc1483: return TCPDLinkType.ATMRfc1483;
                default: return TCPDLinkType.Null;
            }        
        }

        #endregion

        #region TCPD Functions

        #region TCPD misceleanous functions

        /// <summary>
        /// Converts UnixTime to output variables used in TCPD Packet Header
        /// </summary>
        /// <param name="tspan">Input timestamp representing units since beginning of UNIX Epoch</param>
        /// <param name="seconds">Output number of seconds since 1970-01-01</param>
        /// <param name="microseconds">Output number of microseconds since 1970-01-01</param>
        private static void ConvertUnixTimeToTCPD(TimeSpan tspan, out UInt32 seconds, out UInt32 microseconds)
        {
            //PrintDebug(tspan.Ticks + "-->" + (UInt32)(tspan.Ticks / 10000000) + "." + microseconds);
            seconds = (UInt32)(tspan.Ticks / 10000000);
            microseconds = (UInt32)(tspan.Ticks - ((tspan.Ticks / 10000000) * 10000000)) / 10;
        }

        /// <summary>
        /// Converts DateTime to amount of ticks since beginning of UNIX Expoch 1970-01-01
        /// </summary>
        /// <param name="current">Input DateTime varibale which measure time since 0001-01-01</param>
        /// <returns>Returns number of ticks since 1970-01-01</returns>
        private static TimeSpan ConvertDateToUnixTime(DateTime current)
        {
            //PrintError(current.Subtract(new DateTime(1970, 1, 1)).Ticks + "-->" + new TimeSpan(current.Ticks - new DateTime(1970, 1, 1).Ticks).Ticks);
            return new TimeSpan(current.Ticks - new DateTime(1970, 1, 1).Ticks);
        }

        /// <summary>
        /// Converts TCPD Packet Header time information into TimeSpan variable
        /// </summary>
        /// <param name="seconds">Input seconds</param>
        /// <param name="microseconds">Input microseconds. To get ticks it should be multiplied with 10.</param>
        /// <returns>Returns timestamp representing number of units since 1970-01-01</returns>
        private static TimeSpan ConvertTCPDToUnixTime(Int32 seconds, Int32 microseconds)
        {
            //var ts = new TimeSpan(0, 0, 0, seconds).Add(new TimeSpan(microseconds * 10));            
            //PrintDebug(seconds + "." + microseconds + "-->" + ts.Ticks);
            return new TimeSpan(0, 0, 0, seconds).Add(new TimeSpan(microseconds * 10));
        }

        /// <summary>
        /// Converts TimeSpan representing units since begeinning of Unix Epoch into real DateTime
        /// </summary>
        /// <param name="tspan">Input timespan</param>
        /// <returns>Returns real DateTime variable</returns>
        private static DateTime ConvertUnixTimeToDate(TimeSpan tspan)
        {
            //var dt = new DateTime(1970, 1, 1).Add(tspan).Subtract(new DateTime(1970,1,1));            
            //PrintDebug(tspan.Ticks + "-->" + dt.Ticks);
            return new DateTime(1970, 1, 1).Add(tspan);
        }

        /// <summary>
        /// Generate TCP Dump LibPCAP format output file
        /// </summary>
        /// <param name="outFile">File name or file path that will be converted to absolute file path</param>
        /// <returns></returns>
        public bool CreateTCPDOutput(String outFile)
        {
            try
            {
                var bw = new BinaryWriter(File.Open(Path.GetFullPath(outFile), FileMode.Create));
                /* TCP Dump format global header variables */
                const UInt32 magicNumber = 0xa1b2c3d4;
                const UInt16 versionMaj = 0x2;
                const UInt16 versionMin = 0x4;
                /* Use first record as refernce TZI offset */
            //    Int32 thisZoneSec = timeZoneOffset; // no conversion needed
                // var linkType = (UInt32)Frames.First().PmPcapFile.TCPDLinkType;
                Int32 thisZoneSec = 0;
                TCPDLinkType fileLinkType = TCPDLinkType.Null;
                PmSupportedTypes.LinkTypes referenceLinkType = PmSupportedTypes.LinkTypes.Null;
                if (Frames.Any())
                {
                    referenceLinkType = Frames.First().LinkType();
                    fileLinkType = ConvertCommonLayer2ToPcapLayer2(referenceLinkType);
                }
                var linkTypeValue = (UInt32)fileLinkType;
                const UInt32 sigFigs = 0;
                const uint snapLen = 0x0000ffff;
                //Console.WriteLine("{0:x}|{1:x}|{2:x}|{3:x}|{4:x}|{5:x}|{6:x}", magicNumber, versionMaj, versionMin, thisZoneSec, sigFigs, snapLen, linkType);
                /* Write Global Header */
                bw.Write(magicNumber);
                bw.Write(versionMaj);
                bw.Write(versionMin);
                bw.Write(thisZoneSec);
                bw.Write(sigFigs);
                bw.Write(snapLen);
                bw.Write(linkTypeValue);
                /* Write frames */
                foreach (var fr in Frames.Where(fr => fr.LinkType() == referenceLinkType))
                {
                    /* Convert DateTime timestamp to TCPD variables used in TCPD Packet Header*/
                    UInt32 sec;
                    UInt32 usec;
                    ConvertUnixTimeToTCPD(ConvertDateToUnixTime(fr.TimeStamp()), out sec, out usec);

                    bw.Write(sec);
                    bw.Write(usec);
                    bw.Write(fr.IncludedLength());
                    bw.Write(fr.OriginalLength());
                   // bw.Write(GetTCPDPckFrameData(fr));
                    bw.Write(fr.Data());
                }
                bw.Close();
            }
            /* If anything went bad generate exception and return false */
            catch (Exception ex)
            {
                PmConsolePrinter.PrintError(ex.Message);
                return false;
            }
            /* otherwise return true if everything went good */
            return true;
        }

        #endregion

        #region TCPD Global Header parsing Fuctions

        /// <summary>
        /// Reads major version value on 4th byte of TCPD Header  
        /// </summary>
        /// <returns>Returns major number of version in header of TCPD file</returns>
        private UInt16 GetTCPDHdrVersionMaj()
        {
            //binreader.BaseStream.Position = 4;
            BinReader.BaseStream.Seek(4, SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads minor version value on 6th byte of TCPD Header 
        /// </summary>
        /// <returns>Returns minor number of version in header of TCPD file</returns>
        private UInt16 GetTCPDHdrVersionMin()
        {
            //binreader.BaseStream.Position = 6;
            BinReader.BaseStream.Seek(6, SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads UTC to local time correction on 8th byte of TCPD Header
        /// </summary>
        /// <returns>Returns correction time in seconds between UTC and time when capture was taken</returns>
        private Int32 GetTCPDHdrTimezoneOffset()
        {
            //binreader.BaseStream.Position = 8;
            BinReader.BaseStream.Seek(8, SeekOrigin.Begin);
            return BinReader.ReadInt32();
        }

        /// <summary>
        /// Reads accuracy of timestamps on 12th byte of TCPD Header. In practice all tools set it to 0.
        /// </summary>
        /// <returns>Returns usually 0, everything else is suspicious</returns>
        private UInt32 GetTCPDHdrSigfigs()
        {
            //binreader.BaseStream.Position = 12;
            BinReader.BaseStream.Seek(12, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads snapshot length (internal limiter of frames content) on 16th byte of TCPD Header. In practice all tools set it to 0.
        /// </summary>
        /// <returns>Returns snapshot length frame limiter. If unlimited then it returns 0xffff (65535)</returns>
        private UInt32 GetTCPDHdrSnapLength()
        {
            //binreader.BaseStream.Position = 16;
            BinReader.BaseStream.Seek(16, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads layer 2 link type on 20th byte of TCPD Header. In practice all tools set it to 0.
        /// </summary>
        /// <returns>Returns TCPDLinkType enumeration of layer 2 media on which capture was taken</returns>
        private TCPDLinkType GetTCPDHdrNetwork()
        {
            //binreader.BaseStream.Position = 20;
            BinReader.BaseStream.Seek(20, SeekOrigin.Begin);
            return (TCPDLinkType)BinReader.ReadUInt32();
        }


        #endregion

        #region TCPD Packet Header parsing functions

        /// <summary>
        /// Reads seconds in timestamp information starting on zero B of TCPD Packet Header
        /// </summary>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <returns>Number of seconds since start of UNIX Epoch</returns>
        internal UInt32 GetTCPDPckTimestampSeconds(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset;
            BinReader.BaseStream.Seek(offset, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads seconds in timestamp information starting on 4th of TCPD Packet Header
        /// </summary>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <returns>Number of microseconds since start of UNIX Epoch</returns>
        internal UInt32 GetTCPDPckTimestampUseconds(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset + 4;
            BinReader.BaseStream.Seek(offset + 4, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads included frame size starting on 8th B in TCPD Packet Header
        /// </summary>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <returns>Included frame length, that is lesser-equal to potential snapshot length.</returns>
        private UInt32 GetTCPDPckIncludedLength(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset + 8;
            BinReader.BaseStream.Seek(offset + 8, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads original frame size as it was received by network interface card starting on 12th B in TCPD Packet Header
        /// </summary>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <returns>Original frame length as it was presented on the wire.</returns>
        private UInt32 GetTCPDPckOriginalLength(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset + 12;
            BinReader.BaseStream.Seek(offset + 12, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

       /* /// <summary>
        /// Reads frame data starting on 16B in TCPD Packet Header
        /// </summary>
        /// <param name="binreader">Binary reader with opened PCAP file</param>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <param name="frameLength">Frame size representing number of bytes to read</param>
        /// <returns>Byte array filled with binary frame data</returns>
        private byte[] GetTCPDPckFrameData(PmFrame fr)
        {
            //binreader.BaseStream.Position = offset + 16;
            BinReader.BaseStream.Seek(fr.FrameOffset + 16, SeekOrigin.Begin);
            return BinReader.ReadBytes((int)fr.IncludedLength);
        }*/

        /// <summary>
        /// Fills output with appropriate variables from TCPD Packet Header
        /// </summary>
        /// <param name="fr">Target frame</param>
        /// <param name="tsSec">Number of units since beginning of UNIX epoch in seconds</param>
        /// <param name="tsUsec">Number of units since beginning of UNIX epoch in microseconds</param>
        /// <param name="inclLen">Frame length of frame data presented in TCP PCAP File</param>
        /// <param name="origLen">Original frame length as it was received by computer's NIC</param>
        /// <param name="frameData">Binary frame data of inclLen size</param>
        private void GetTCPDPckPacketHeaderRecord(IPmFrame fr, out UInt32 tsSec, out UInt32 tsUsec, out UInt32 inclLen, out UInt32 origLen, out byte[] frameData)
        {
            tsSec = GetTCPDPckTimestampSeconds(fr.FrameOffset());
            tsUsec = GetTCPDPckTimestampUseconds(fr.FrameOffset());
            inclLen = GetTCPDPckIncludedLength(fr.FrameOffset());
            origLen = GetTCPDPckOriginalLength(fr.FrameOffset());
           // frameData = GetTCPDPckFrameData(fr);
            frameData = fr.Data();
        }

        #endregion

        #region TCPD printing functions

        /// <summary>
        /// Prints all information in TCPD Global Header
        /// </summary>
        public void PrintTCPDGlobalHeader()
        {
            PmConsolePrinter.PrintDebug("GLOBAL HEADER INFORMATION FOR " + Path.GetFileName(FilePath));
            PmConsolePrinter.PrintInfoEol("Version> {0}.{1}", _versionMaj, _versionMin);
            PmConsolePrinter.PrintInfoEol("Correction for UTC> {0}", _tcpdTimeZoneOffset);
            PmConsolePrinter.PrintInfoEol("Sigfigs> {0}", _sigfigs);
            PmConsolePrinter.PrintInfoEol("Snapshot Length> {0}", _snaplen);
            PmConsolePrinter.PrintInfoEol("Data link type> {0}",_pcapLinkType);
        }

        /// <summary>
        /// Prints whole TCPD Packet Header inforamtion of one frame record
        /// </summary>
        /// <param name="fr">Target frame</param>
        public void PrintTCPDPacketHeaderRecord(IPmFrame fr)
        {
            UInt32 tsSec;
            UInt32 tsUsec;
            UInt32 inclLen;
            UInt32 origLen;
            byte[] frameData;

            GetTCPDPckPacketHeaderRecord(fr, out tsSec, out tsUsec, out inclLen, out origLen, out frameData);

            PmConsolePrinter.PrintInfoEol("Time span in seconds.useconds> {0}.{1}", tsSec, tsUsec);
            PmConsolePrinter.PrintInfoEol("Converted time> {0}", ConvertUnixTimeToDate(ConvertTCPDToUnixTime((int)tsSec, (int)tsUsec)).ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));

            PmConsolePrinter.PrintInfoEol("Included frame length> ");
            if (inclLen > _snaplen) PmConsolePrinter.PrintError(inclLen.ToString(CultureInfo.InvariantCulture));
            else Console.WriteLine("{0}",inclLen);

            PmConsolePrinter.PrintInfoEol("Original frame length> {0}", origLen);
            PmConsolePrinter.PrintInfoEol("Frame data> {0}", frameData);
        }

        /// <summary>
        /// Prints whole frame table from TCPD PCAP file
        /// </summary>
        public void PrintTCPDFrameTable()
        {
            foreach (var fr in Frames)
            {
                PmConsolePrinter.PrintDebug("FRAME " + Frames.IndexOf(fr) + " OFFSET " + fr.FrameOffset());
                PrintTCPDPacketHeaderRecord(fr);
            }
        }

        #endregion

        #region TCPD parsing function

        /// <summary>
        /// Parses MNM Capture File Header and initialize all basic MNM properties of Sniff File Specification
        /// </summary>
        internal void ParseTCPDHeader()
        {
            _versionMaj = GetTCPDHdrVersionMaj();
            _versionMin = GetTCPDHdrVersionMin();
            _tcpdTimeZoneOffset = GetTCPDHdrTimezoneOffset();
            _sigfigs = GetTCPDHdrSigfigs();
            _snaplen = GetTCPDHdrSnapLength();
            _pcapLinkType = GetTCPDHdrNetwork();
            //PrintTCPDGlobalHeader(sfs);            
        }

        /// <summary>
        /// Generate next frame offset via equation = offset + 16B + included frame length
        /// </summary>        
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <param name="length"></param>
        /// <returns>Returns offset of next frame</returns>
        private UInt32 CountTCPDNextFrameOffset(UInt32 offset, UInt32 length)
        {
            //return offset + 16 + GetTCPDPckIncludedLength(binreader, offset);
            return offset + 16 + length;
        }

/*
        /// <summary>
        /// Generate next frame offset via equation = offset + 16B + included frame length, this variant includes binary reading operation
        /// </summary>
        /// <param name="offset">Starting offset of target frame in TCPD PCAP file</param>
        /// <returns></returns>
        private UInt32 CountTCPDNextFrameOffset(UInt32 offset)
        {
            return offset + 16 + GetTCPDPckIncludedLength(offset);
        }
*/

        /// <summary>
        /// Parses frame table of TCPD PCAP file and more importantly it initialize TCPFrameTable variable where are stored all frame offsets
        /// </summary>
        private void ParseTCPDFrameTable()
        {
            if (!PmSupportedTypes.IsSupportedLinkType(linkType))
                return;

            //sfs.Frames.Clear();
            /* First frame starts always on 24th byte of TCPD PCAP file. Just after Globap Capture Header */
            //sfs.TCPDFrameTable = new List<uint> {24};
            UInt32 last = 24;
            var velikost = BinReader.BaseStream.Length;
            do
            {
                var len = GetTCPDPckIncludedLength(last);
                Frames.Add(new PmFramePcap(BinReader,
                                           last,
                                           ConvertUnixTimeToDate(
                                              ConvertTCPDToUnixTime(
                                                                    (Int32)(GetTCPDPckTimestampSeconds(last)+_tcpdTimeZoneOffset),
                                                                    (Int32)GetTCPDPckTimestampUseconds(last)
                                                                   )
                                                                ),
                                           linkType,
                                           len,
                                           GetTCPDPckOriginalLength(last)
                                   )
                          );
                last = CountTCPDNextFrameOffset(last, len);
            } while (last != velikost);
            /* Remove last record because it is unparsable EOF */
            //sfs.TCPDFrameTable.Remove(sfs.TCPDFrameTable.Last());
            //PrintError(sfs.TCPDFrameTable.Count.ToString());
            //PrintError("xxx"+sfs.Frames.Count.ToString());
        }

        #endregion

        #endregion

    }
}
